package com.marvels.common.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.marvels.common.constants.MarvelsConstant;
import com.marvels.common.dto.ChannelUserDTO;
import com.marvels.common.dto.UserDTO;
import com.marvels.common.exception.MarvelsBusinessException;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;

/**
 * 缓存工具类
 * @author wangliang
 *
 */
@Component
public class CacheUtils {
	
	private static RedisTemplate redisTemplate;
	
	private static String secretKey;
	
	private static List<String> ignoreUserCode;
	
	private static String configPropertiesPath;
	
	private long ttlMillis = 15*60*1000l;
	
	private static final CacheUtils cacheUtils = new CacheUtils();
	
	@Value("${marvels.properties.salt}")
	public void setSecretKey(String secretKey) {
		CacheUtils.secretKey = secretKey;
	}
	
	@Value("${config.properties.path}")
	public void setConfigPropertiesPath(String configPropertiesPath) {
		CacheUtils.configPropertiesPath = configPropertiesPath;
	}
	
	public static List<String> getIgnoreUserCode(){
		if(CacheUtils.ignoreUserCode == null){
			String ignoreUserCodes= ServerProperties.getValue(configPropertiesPath, "common.properties", "ignore_user_code");
			if(StrUtil.isNotEmpty(ignoreUserCodes)){
				CacheUtils.ignoreUserCode = Arrays.asList(ignoreUserCodes.split(","));
			}
		}
		return CacheUtils.ignoreUserCode;
	}
	
	
	@Resource(name="redisTemplate")
	public void setRedisTemplate(RedisTemplate redisTemplate) {
		CacheUtils.redisTemplate = redisTemplate;
	}

	private static String createMarvelsToken(UserDTO user) throws MarvelsBusinessException{
		if(user == null){
			throw new MarvelsBusinessException("无法创建token, user为空");
		}
		if(StringUtils.isEmpty(user.getUserCode())){
			throw new MarvelsBusinessException("无法创建token, 用户编码为空");
		}
		Object o = ThreadContextUtil.get(MarvelsConstant.USER_IP);
		if(o == null){
			throw new MarvelsBusinessException("非法用户请求，没有请求IP");
		}
		String userIp = (String)o;
		if(StringUtils.isEmpty(userIp)){
			throw new MarvelsBusinessException("非法用户请求，没有请求IP");
		}
		String key = generateKey(userIp,user.getUserCode());
		return ShaUtils.Encrypt(key);
	}
	/**
	 * true 则表示值能被覆盖  没有锁   false 有锁
	 * @param lockName
	 * @param consumerName
	 * @return
	 */
	public static Boolean isNotLock(String lockName, String consumerName){
		return redisTemplate.opsForValue().setIfAbsent(lockName, consumerName);
	}
	
	/**
	 * 设置值得有效性
	 * @param lockName
	 * @param seconds
	 * @return
	 */
	public static Boolean setExpire(String lockName,int seconds){
		return redisTemplate.expire(lockName, seconds, TimeUnit.SECONDS);
	}
	
	
	/**
	 * 删除key
	 * @param lockName
	 * @param consumerName
	 */
	public static void del(String lockName){
		redisTemplate.delete(lockName);
	}
	private static String generateKey(String userIp,String userCode){
		StringBuilder key = new StringBuilder();
		key.append(userIp).append(userCode).append(secretKey);
		return key.toString();
	}
	
	/**
	 * 判断用户是否已登录
	 * @return
	 * @throws MarvelsBusinessException 
	 */
	public static boolean isLogin(){
		Object o = ThreadContextUtil.get(MarvelsConstant.TOKEN);
		if(o == null){
			return false;
		}
		String token = (String)o;
		if(StringUtils.isEmpty(token)){
			return false;
		}
		try {
//			Jws<Claims> claims = checkToken(token);
//			String subject = claims.getBody().getSubject();
//			UserDTO user =  (UserDTO) redisTemplate.opsForValue().get(token);
//			if(user == null){
//				return false;
//			}
//			if(!subject.equals(user.getUserCode())){
//				return false;
//			}
			return checkMarvelsToken(token);
		} catch (Exception e) {
			MarvelsLogUtil.getInstance().error(e.getMessage(),e);
		    return false;
		}
	}
	
	/**
	 * 刷新登陆时效
	 * @param token
	 * @return
	 */
	public static boolean refreshLoginTime(String token){
		try {
			// 登录时效默认1小时
			String timeout= ServerProperties.getValue(configPropertiesPath, "common.properties", "login.timeout");
			String timeUnit= ServerProperties.getValue(configPropertiesPath, "common.properties", "login.timeunit");
			timeout = StrUtil.nullToDefault(timeout, "1");
			timeUnit = StrUtil.nullToDefault(timeUnit, "HOURS");
			if("-1".equals(timeout)){
				// -1为永久缓存
				redisTemplate.expire(token, Long.parseLong(timeout), EnumUtil.fromString(TimeUnit.class, timeUnit));
			}else{
				redisTemplate.expire(token, Long.parseLong(timeout), EnumUtil.fromString(TimeUnit.class, timeUnit));
			}
		} catch (Exception e) {
			MarvelsLogUtil.getInstance().error(token + ",刷新登陆时效异常", e);
			return false;
		}
		return true;
	}


	/**
	 * 校验token是否非法
	 * @param token
	 * @return
	 * @throws SignatureException
	 */
//	private static Jws<Claims> checkToken(String token)throws MarvelsBusinessException{
//		try{
//			if(StringUtils.isEmpty(token)){
//				throw new MarvelsBusinessException("token为空");
//			}
//			MarvelsLogUtil.getInstance().info("token : "+token);
//			return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
//		}catch(ExpiredJwtException e){
//			throw new MarvelsBusinessException("token过期, 请重新登录. "+e.getMessage());
//		}catch(MalformedJwtException e){
//			throw new MarvelsBusinessException("非法token. "+e.getMessage());
//		}catch(SignatureException e){
//			throw new MarvelsBusinessException("非法token. "+e.getMessage());
//		}catch(IllegalStateException e){
//			throw new MarvelsBusinessException("非法token. "+e.getMessage());
//		}catch(UnsupportedJwtException e){
//			throw new MarvelsBusinessException("非法token. "+e.getMessage());
//		}catch(PrematureJwtException e){
//			throw new MarvelsBusinessException("非法token. "+e.getMessage());
//		}
//	}
	
	private static boolean checkMarvelsToken(String token)throws MarvelsBusinessException{
		if(StringUtils.isEmpty(token)){
			throw new MarvelsBusinessException("token为空");
		}
		Object loginUUid = ThreadContextUtil.get(MarvelsConstant.LOGINUUID);
		if(loginUUid == null){
			throw new MarvelsBusinessException("loginUUid为空");
		}
		try{
			UserDTO user =  (UserDTO) redisTemplate.opsForValue().get(token);
			if(user == null){
				return false;
			}
			//忽略用户之外的用户才做登录唯一验证
			if(getIgnoreUserCode().isEmpty() || (!getIgnoreUserCode().contains(user.getUserCode()))){
				if(!user.getLoginUUid().equals(loginUUid.toString())){
					return false;
				}
			}
			/*String requestToken = createMarvelsToken(user);
			if(token.equals(requestToken)){
				//更新缓存用户时间
				redisTemplate.opsForValue().set(token, user);
				ThreadContextUtil.set(MarvelsConstant.USER, user);
				return true;
			}
			return false;*/
			return true;
		}catch(Exception e){
			MarvelsLogUtil.getInstance().error("从redis获取用户失败: ",e);
		    return false;
		}
	}
	
	/**
	 * 缓存用户登录信息
	 * @param user
	 * @throws MarvelsBusinessException 
	 */
	@SuppressWarnings("unused")
	public static void cacheUser(UserDTO user, String platformNameCode) throws MarvelsBusinessException{
		if(user == null){
			MarvelsLogUtil.getInstance().error("登录用户缓存失败, user is null");
			throw new MarvelsBusinessException("登录用户缓存失败, user is null");
		}
		try{
			//创建用户token
			/*String token = createMarvelsToken(user);
			user.setToken(token);*/
			
			if(user == null){
				throw new MarvelsBusinessException("无法创建token, user为空");
			}
			if(StringUtils.isEmpty(user.getUserCode())){
				throw new MarvelsBusinessException("无法创建token, 用户编码为空");
			}
			Object o = ThreadContextUtil.get(MarvelsConstant.USER_IP);
			if(o == null){
				throw new MarvelsBusinessException("非法用户请求，没有请求IP");
			}
			String userIp = (String)o;
			if(StringUtils.isEmpty(userIp)){
				throw new MarvelsBusinessException("非法用户请求，没有请求IP");
			}
			String key = generateKey(platformNameCode, user.getUserCode());
			String token = ShaUtils.Encrypt(key);
			String loginUUid = RandomUtil.simpleUUID();
			user.setToken(token);
			user.setLoginUUid(loginUUid);
			
			// 登录时效默认1小时
			String timeout= ServerProperties.getValue(configPropertiesPath, "common.properties", "login.timeout");
			String timeUnit= ServerProperties.getValue(configPropertiesPath, "common.properties", "login.timeunit");
			timeout = StrUtil.nullToDefault(timeout, "1");
			timeUnit = StrUtil.nullToDefault(timeUnit, "HOURS");
			if("-1".equals(timeout)){
				// -1为永久缓存
				redisTemplate.opsForValue().set(token, user);
			}else{
				redisTemplate.opsForValue().set(token, user, Long.parseLong(timeout), EnumUtil.fromString(TimeUnit.class, timeUnit));
			}
			
		}catch(Exception e){
			MarvelsLogUtil.getInstance().error("登录用户缓存失败 ", e);
			throw new MarvelsBusinessException("登录用户缓存失败",e);
		}
	}
	
	/**
	 * 获取用户登录缓存
	 * @return
	 * @throws MarvelsBusinessException 
	 */
	public static UserDTO getCacheUser() throws MarvelsBusinessException{
		UserDTO user = null;
		//先从线程上下文中取
		Object o = ThreadContextUtil.get(MarvelsConstant.USER);
		if(o != null){
			return (UserDTO)o;
		}
		//没有再从redis中取
		o = ThreadContextUtil.get(MarvelsConstant.TOKEN);
		if(o == null){
			//throw new MarvelsBusinessException("Thread Local中没有token");
			MarvelsLogUtil.getInstance().warn("Thread Local中没有token");
			return null;
		}
		String token = (String)o;
		try{
			if(!checkMarvelsToken(token)){
				throw new MarvelsBusinessException("用户请求令牌和登录令牌不一致");
			}
			user = (UserDTO) (redisTemplate.opsForValue().get(token));
			ThreadContextUtil.set(MarvelsConstant.USER,user);
		}catch(ExpiredJwtException e){
			throw new MarvelsBusinessException("token过期, 请重新登录");
		}catch(Exception e){
			MarvelsLogUtil.getInstance().error(e.getMessage(),e);
			throw new MarvelsBusinessException("非法token");
		}
		return user;
	}
	
	/**
	 * 删除缓存中的用户
	 * @throws MarvelsBusinessException
	 */
	public static void delCacheUser() {
		Object o = ThreadContextUtil.get(MarvelsConstant.TOKEN);
		if(o == null){
			return;
		}
		Object loginUUid = ThreadContextUtil.get(MarvelsConstant.LOGINUUID);
		if(loginUUid == null){
			return;
		}
		UserDTO user =  (UserDTO) redisTemplate.opsForValue().get(o.toString());
		//是当前登录用户才允许删除
		if(user != null && user.getLoginUUid().equals(loginUUid.toString())){
			String token = (String)o;
			redisTemplate.opsForValue().getOperations().delete(token);
		}
	}

	/**
	 * 删除KEY
	 * 
	 * @param key
	 */
	public static void delete(String key) {
		redisTemplate.opsForValue().getOperations().delete(key);
	}

	/**
	 * 缓存数据到redis
	 * @param key 键
	 * @param value 值
	 */
	public static void cacheObject(String key,Object value){
		redisTemplate.opsForValue().set(key, value);
	}

	/**
	 * 缓存字符串到redis
	 * 
	 * @param key
	 * @param value
	 */
	public static void cacheString(String key, String value) {
		redisTemplate.opsForValue().set(key, value);
	}

	/**
	 * 缓存字符串到redis（设置失效时间）
	 * 
	 * @param key
	 * @param value
	 */
	public static void cacheString(String key, String value, long time) {
		redisTemplate.opsForValue().set(key, value ,time,TimeUnit.SECONDS);
	}
	
	/**
	 * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作
	 * @param key
	 * @param delta
	 */
	public static Long increment(String key, long delta) {
		return redisTemplate.opsForValue().increment(key, delta);
	}

	/**
	 * 获取redis缓存字符串
	 * 
	 * @param key
	 * @return
	 */
	public static String getCacheString(String key) {
		Object obj = redisTemplate.opsForValue().get(key);
		return obj == null ? null : obj.toString();
	}
	
	/**
	 * 缓存数据到redis（设置失效时间）
	 * @param key 键
	 * @param value 值
	 */
	public static void cacheObject(String key,Object value, long time){
		redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
	}
	
	/**
	 * 获取redis缓存数据
	 * @param key
	 * @return
	 */
	public static Object getCacheObject(String key) {
		return redisTemplate.opsForValue().get(key);
	}
	/**
	 * 校验前端接口token及用户
	 * @param accessToken
	 * @param idFintechUmUser
	 * @return
	 */
	public static ChannelUserDTO verifyUser(String accessToken, String idFintechUmUser) {
		if ((StringUtils.isEmpty(accessToken)) || (StringUtils.isEmpty(idFintechUmUser))) {
			return null;
		}
		String userInfo = null;
		try {
			userInfo = (String) redisTemplate.opsForValue().get(idFintechUmUser);
		} catch (Exception e) {
			MarvelsLogUtil.getInstance().error("redis获取用户信息异常");
		}

		if (StringUtils.isEmpty(userInfo)) {
			MarvelsLogUtil.getInstance().info("redis中用户信息为空");
			return null;
		}
		JSONObject jsonObject = JSONObject.parseObject(userInfo);
		if (!"3".equals(jsonObject.getString("userType"))) {
			if (!accessToken.equals(jsonObject.get("token"))) {
				return null;
			}
		}
		redisTemplate.opsForValue().set(idFintechUmUser, userInfo, 1296000l);
		ChannelUserDTO user = JSONObject.toJavaObject(jsonObject, ChannelUserDTO.class);
		return user;
	}
	
}
